package cate.game.role.base;

import cate.common.game.GameCollection;
import cate.common.table.d.GDFunc;
import cate.common.table.d.GDObj;
import cate.common.table.d.GDOperation;
import cate.common.table.role.RoleLevelRow;
import cate.common.table.role.vip.RoleVipRow;
import cate.game.GameBody;
import cate.game.attr.FightAttr;
import cate.game.client.msg.MergeMsgSender;
import cate.game.event.master.LevelChangeEvent;
import cate.game.event.master.PowerChangeEvent;
import cate.game.event.master.VipChangeEvent;
import cate.game.mail.po.MailBuilder;
import cate.game.res.MixRes;
import cate.game.role.Role;
import cate.game.role.RoleMergeHandler;
import cate.game.role.RolePart;
import cate.game.role.actor.RoleActor;
import cate.game.role.base.msg.RoleLevelUpdateResp;
import cate.game.role.base.msg.RoleVipUpdateResp;
import cate.game.role.login.RoleLogin;
import cate.game.role.mail.Mail;
import cate.game.role.show.avatar.AvatarVO;
import cate.game.role.show.bubble.BubbleBoxVO;
import cate.game.role.show.frame.HeadFrameVO;
import cate.game.role.show.icon.HeadIconVO;
import cate.game.role.show.title.TitleVO;
import cate.game.role.show.vehicle.VehicleSkinVO;
import cate.game.role.world.invite.RoleInvite;
import cate.game.stat.item.StatItemIOEntity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import cp.solution.business.sys.account.AccountEntity;
import cp.solution.business.sys.account.dto.AccountLoginData;
import easy.java.dev.note.NoteField;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;

@Slf4j
@Document(collection = GameCollection.NAME_ROLE_BASE)
@ToString
public class RoleBase extends RolePart {
    @NoteField("账户ID")
    public int accountId;

    @NoteField("openID")
    public String openId;

    @NoteField("访客ID")
    public String visitorId;

    @NoteField("gameID")
    public String gameId;

    @NoteField("渠道编码")
    public String channelCode;

    @NoteField("当前游戏服ID")
    public int srvId;

    @NoteField("原始游戏服ID")
    public int srvIdOrigin;

    @NoteField("角色名")
    public String name;

    @NoteField("角色性别")
    public byte sex;

    @NoteField("角色等级")
    public int level;

    @NoteField("主角经验")
    public long exp;

    @NoteField("主角阶段")
    public int grade;

    @NoteField("角色VIP等级")
    public int vip;

    @NoteField("VIP积分")
    public long vipPoint;

    @NoteField(value = "角色头像")
    public HeadIconVO headIcon;

    @NoteField("头像框")
    public HeadFrameVO headFrame;

    @NoteField("冒险形象")
    public AvatarVO avatar;

    @NoteField("称号")
    public TitleVO title;

    @NoteField("气泡框")
    public BubbleBoxVO bubbleBox;

    @NoteField("载具")
    public VehicleSkinVO vehicle;

    @NoteField("所在省")
    public int province;

    @NoteField("所在市")
    public int city;

    @NoteField("备注")
    public String note;

    @NoteField("主线战力")
    public long power;

    @NoteField("最高战力")
    public long powerMax;

    @NoteField("当前语言编码")
    public String langCode;

    @NoteField("主角皮肤")
    public int leaderSkin;

    @NoteField("登录数据")
    public RoleLogin login;

    @NoteField("托号数据")
    public RoleActor actor;

    @NoteField(value = "不重要的数据", detail = "不影响玩家核心利益的数据")
    public RoleUnimportant uni;

    @NoteField("分享邀请引流数据")
    public RoleInvite invite;

    // 是否需要 处理 自身的 合服情况
    @Transient
    public transient RoleMergeHandler merge = new RoleMergeHandler();

    public RoleBase() {

    }

    public RoleBase(Role role) {
        super(role);
    }

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (level == 0) {
            level = 1;
        }
        if (grade == 0) {
            grade = 1;
        }
        if (headIcon == null) {
            headIcon = new HeadIconVO();
        }
        if (headFrame == null) {
            headFrame = new HeadFrameVO();
        }
        if (avatar == null) {
            avatar = new AvatarVO();
        }
        if (title == null) {
            title = new TitleVO();
        }
        if (bubbleBox == null) {
            bubbleBox = new BubbleBoxVO();
        }
        if (vehicle == null) {
            vehicle = new VehicleSkinVO();
        }
        if (province <= 0 || city <= 0) {
            province = role.getGame().table.area.province.random();
            city = role.getGame().table.area.city.random(province);
        }
        if(login == null) {
            login = new RoleLogin();
        }
        if(actor == null) {
            actor = new RoleActor();
        }
        final int srvIdCur = role.getGame().config.srvId;
        if(level > 1 && this.srvId != srvIdCur) {
            this.merge.happen(this.srvId, srvIdCur);
            log.info("角色需要处理合服逻辑 从{}合到{} @ {} {}", this.srvId, srvIdCur, this.uid, this.name);
        }
        this.srvId = srvIdCur;
        //
        if(uni == null) {
            uni = new RoleUnimportant();
        }
        if (invite == null) {
            invite = new RoleInvite();
        }
        invite.initialize(role);
    }

    @Override
    public void onCreate()  {
        invite.onCreate();
    }

    @Override
    public void onEnter() {
        invite.onEnter();
    }

    @Override
    public void onDaySpan(boolean silence) {
        invite.onDaySpan(silence);
    }

    public RoleBase copy() {
        RoleBase cp = new RoleBase();
        cp.accountId = this.accountId;
        cp.srvId = this.srvId;
        cp.srvIdOrigin = this.srvIdOrigin;
        cp.grade = this.grade;
        cp.level = this.level;
        cp.exp = this.exp;
        cp.headIcon = this.headIcon == null ? null : this.headIcon.copy();
        cp.headFrame = this.headFrame == null ? null : headFrame.copy();
        cp.avatar = this.avatar == null ? null : avatar.copy();
        cp.title = this.title == null ? null : title.copy();
        cp.bubbleBox = this.bubbleBox == null ? null : bubbleBox.copy();
        cp.vehicle = this.vehicle == null ? null : vehicle.copy();
        cp.province = this.province;
        cp.city = this.city;
        cp.name = this.name;
        cp.power = this.power;
        cp.powerMax = this.powerMax;
        cp.sex = this.sex;
        cp.uid = this.uid;
        cp.vip = this.vip;
        cp.note = this.note;
        cp.vipPoint = this.vipPoint;
        cp.leaderSkin = this.leaderSkin;
        cp.login = this.login;
        // cp.actor = this.actor; 托号数据没必要拷贝
        return cp;
    }

//    public void read(InstanceRsRow row){
//        this.level = row.level;
//        this.name = row.name;
//        this.sex = row.gender;
//        this.headIcon = new HeadIconVO(row.headIcon);
//        this.headFrame = new HeadFrameVO(row.headFrame);
//        this.avatar = new AvatarVO(row.avatarId);
//    }

    /**
     * 增加经验
     *
     */
    public void addExp(long add) {
        addExp(add, null);
    }

    /**
     * 增加经验
     *
     */
    public void addExp(long add, MergeMsgSender sender) {
        int tarLevel = this.level;
        // 可用来扣减的经验
        long expToCost = this.exp + add;
        // 累计升级导致的扣减经验
        long upToCost = 0;
        RoleLevelRow tpl = role.getGame().table.role.level.getByLevel(tarLevel);
        while (tpl != null && expToCost > 0) {
            if (expToCost < tpl.upExp) {
                break;
            }
            RoleLevelRow nextTpl = role.getGame().table.role.level.getByLevel(tarLevel + 1);
            if (nextTpl == null) {
                break;
            }
            upToCost += tpl.upExp;
            expToCost -= tpl.upExp;
            tarLevel++;
            tpl = nextTpl;
        }
        if (tarLevel > this.level) {
            // 升级
            this.exp += add;
            this.exp -= upToCost;
            levelUp(tarLevel - this.level, sender);
            updatePower();
        } else {
            // 只加经验,不升级
            this.exp += add;
            noticeUpdate(null);
        }
    }

    public void levelUp(int add) {
        levelUp(add,null);
    }

    /**
     * 主角升级
     * 只能gm工具调用
     */
    public void levelUp(int add, MergeMsgSender sender) {
        int levelOld = this.level;
        this.level += add;
        MixRes reward = new MixRes();
        for (int lv = levelOld; lv < this.level; lv++) {
            RoleLevelRow tpl = role.getGame().table.role.level.getByLevel(lv);
            if (StringUtils.isNotBlank(tpl.rewardStr)) {
                reward.addList(new MixRes(tpl.rewardStr));
            }
        }
        reward.add(role, sender);
        role.getHistory().action.levelChange(this.level);
        noticeUpdate(sender);
        publishEvent(role, new LevelChangeEvent(role, this.level, this.level - add));
    }

    public void noticeVipUpdate(){
        role.sendNow(new RoleVipUpdateResp(this.vip, this.vipPoint));
    }

    public void noticeUpdate(MergeMsgSender sender) {
        MergeMsgSender.handle(sender, new RoleLevelUpdateResp(this.level, this.exp), role);
    }

    /**
     * 更新主线战力
     */
    public void updatePower() {
        long tmp = role.getGame().power.calcPositionPower(role, GDFunc.ADVENTURE);
        boolean changed = this.power != tmp;
        this.power = tmp;
        long oldPowerMax = this.powerMax;
        powerMax = Math.max(power,powerMax);
        if (changed) {
            publishEvent(role, new PowerChangeEvent(role, this.power));
        }
    }

    @JsonIgnore
    public FightAttr getBaseAttr(GameBody game) {
        FightAttr attr = new FightAttr();
        RoleLevelRow levelRow;
        for (int i = 1; i <= level; ++i) {
            levelRow = game.table.role.level.get(i);
            if(levelRow!=null){
                attr.add(new FightAttr(levelRow.attrStr));
            }
        }
        return attr;
    }

    public void read(AccountLoginData ald) {
        AccountEntity account = ald.getAccount();

        if(StringUtils.isNotBlank(account.getOpenId())) {
            this.openId = account.getOpenId();
        }

        if(StringUtils.isNotBlank(ald.getGameId())) {
            this.gameId = ald.getGameId();
        } else if(StringUtils.isNotBlank(account.getGameId())) {
            this.gameId = account.getGameId();
        }

        if(StringUtils.isNotBlank(ald.getChannelCode())) {
            this.channelCode = ald.getChannelCode();
        } else if(StringUtils.isNotBlank(account.getChannelCode())) {
            this.channelCode = account.getChannelCode();
        }

        if(StringUtils.isNotBlank(account.getIp())) {
            this.login.ip = account.getIp();
        }

        this.login.clientType = ald.getClientType();
    }

    public void addVipPoint(long point){
        /** 增加vip积分 */
        this.vipPoint += point;
        checkVipLevelUp();
        noticeVipUpdate();
        doStat(point);
    }

    private void doStat(long point){
        if (role == null) {
            return;
        }
        StatItemIOEntity po = new StatItemIOEntity();
        po.setItemType(GDObj.Type.NUM);
        po.setItemTid(GDObj.Num.VIP_POINT);
        po.setItemName(GDObj.Num.name(GDObj.Num.VIP_POINT));
        po.setItemNum(point);
        po.setItemNumEnd(this.vipPoint);
        role.getGame().stat.itemRecord(role, po);
    }

    /**
     * 检测当前的vip积分是否可以升级
     */
    private void checkVipLevelUp() {
        RoleVipRow row = role.getGame().table.vip.base.get(vip + 1);
        while (row != null && this.vipPoint >= row.levelUpExp) {
            int oldVip = vip;
            // 升级
            vip++;
            publishEvent(role, new VipChangeEvent(role, oldVip, vip));
            // 升级邮件
            if (row.mailId > 0) {
                Mail mail = new MailBuilder()
                        .setOperation(GDOperation.VIP_LEVEL_REWARD)
                        .setOperationSub(vip)
                        .setContentId(row.mailId)
                        .setReward(StringUtils.isNotBlank(row.levelRewardStr) ? new MixRes(row.levelRewardStr) : null)
                        .build();
                role.getMail().add(mail);
            }
            // 判断下一级能不能继续升上去
            row = role.getGame().table.vip.base.get(vip + 1);
        }
    }

    @JsonIgnore
    @Transient
    public RoleVipRow getVipRow(){
        return role.getGame().table.vip.base.get(vip);
    }
}
